﻿using System;
using System.Web;
using System.Linq;
using System.Web.Security;
using System.Collections.Generic;

using SimpleForum.Data.Models;
using SimpleForum.Data.Repositories;

namespace SimpleForum.Data.Services
{
    public class UserService : IUserService
    {
        private readonly IRepository<User> UserRepository;
        private readonly IRepository<Notification> NotificationRepository;

        public enum ReputationType
        {
            ReplyThread = 1, ReportPost = 1, CreateThread = 5, EarnAchievement = 10
        }

        public enum AchievementType
        {
            NewbieAchievement, CommentatorAchievement, EditorAchievement, ComedianAchievement,
            NeighbourhoodWatchAchievement
        }

        public enum Fields
        {
            Bio
        }

        public UserService(IRepository<User> UserRepository, IRepository<Notification> NotificationRepository)
        {
            this.UserRepository = UserRepository;
            this.NotificationRepository = NotificationRepository;
        }

        public User GetUser(int Id)
        {
            return UserRepository.Get(Id);
        }

        public User GetUserByEmail(string Email)
        {
            return UserRepository.Get().SingleOrDefault(x => x.Email == Email);
        }

        public User GetUserByUsername(string Username)
        {
            return UserRepository.Get().SingleOrDefault(x => x.UserName == Username);
        }

        public IEnumerable<User> GetUsers()
        {
            return UserRepository.Get();
        }

        public void UpdateField(int UserId, Fields Field, string Value)
        {
            var User = GetUser(UserId);

            if (User != null)
            {
                switch (Field)
                {
                    case Fields.Bio:
                        User.Bio = Value;
                    break;
                }

                UserRepository.Update(User);
            }
        }

        public bool Login(string Email, string Password, string Data = "")
        {
            var User = GetUserByEmail(Email);

            if (User != null && User.PasswordHash == FormsAuthentication.HashPasswordForStoringInConfigFile(Password, "SHA1"))
            {
                CreateAuthTicket(Email, Data);

                User.DateLastActivity = DateTime.Now;
                User.DateLastLogin = DateTime.Now;
                UserRepository.Update(User);

                return true;
            }

            return false;
        }

        public void CreateAuthTicket(string Email, string Data = "")
        {
            var Ticket = new FormsAuthenticationTicket(1, Email, DateTime.Now, DateTime.Now.AddMinutes(60), true, Data);
            var Cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(Ticket))
            {
                Expires = Ticket.Expiration,
                Path = FormsAuthentication.FormsCookiePath
            };

            if (HttpContext.Current != null) HttpContext.Current.Response.Cookies.Add(Cookie);
        }

        public void Logout()
        {
            FormsAuthentication.SignOut();
        }

        public User CreateUser(string Username, string Email, string Password, string Avatar, out System.Web.Security.MembershipCreateStatus Status)
        {
            if (GetUserByUsername(Username) != null) { Status = System.Web.Security.MembershipCreateStatus.DuplicateUserName; return null; }
            else if (GetUserByEmail(Email) != null) { Status = System.Web.Security.MembershipCreateStatus.DuplicateEmail; return null; }
            else
            {
                Status = System.Web.Security.MembershipCreateStatus.Success;

                var User = new User
                {
                    AvatarFilename = Avatar,
                    DateCreated = DateTime.Now,
                    DateLastPasswordChange = DateTime.Now,
                    DateLastLogin = DateTime.Now,
                    DateLastActivity = DateTime.Now,
                    Email = Email,
                    Enabled = false,
                    PasswordHash = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(Password, "SHA1"),
                    PasswordSalt = "",
                    UserName = Username
                };

                Utils.Utils.SendEmail(Email, "Account activation for " + ConfigurationService.ForumName, "Please activate your account by clicking on the below URL:");

                return UserRepository.Add(User);
            }
        }

        public void AddReputation(int UserId, ReputationType Type, string URL)
        {
            if (!ConfigurationService.ReputationEnabled) return;

            var User = UserRepository.Get(UserId);
            AddReputation(User, Type, URL);
        }
        public void AddReputation(User User, ReputationType Type, string URL)
        {
            if (!ConfigurationService.ReputationEnabled) return;

            User.Reputation.Add(new Reputation
            {
                ReputationEarned = (int)Type,
                ReputationType = Enum.GetName(typeof(ReputationType), Type),
                URL = URL
            });

            UserRepository.Update(User);
        }

        public void GiveAchievement(int UserId, AchievementType Type)
        {
            if (!ConfigurationService.AchievementsEnabled) return;

            var User = UserRepository.Get(UserId);
            GiveAchievement(User, Type);
        }

        public void GiveAchievement(User User, AchievementType Type)
        {
            if (!ConfigurationService.AchievementsEnabled) return;

            if (!HasAchievement(User, Type))
            {
                User.Achievements.Add(new UserAchievement
                {
                    AchievementName = Enum.GetName(typeof(AchievementType), Type)
                });

                AddNotification(
                    User.Id,
                    String.Format("You have been awarded the {0} Achievement", Enum.GetName(typeof(AchievementType), Type).Replace("Achievement", ""))
                );

                AddReputation(User, ReputationType.EarnAchievement, null);

                UserRepository.Update(User);
            }
        }

        public bool HasAchievement(int UserId, AchievementType Type)
        {
            var User = UserRepository.Get(UserId);

            return HasAchievement(User, Type);
        }

        public bool HasAchievement(User User, AchievementType Type)
        {
            return User.Achievements.Any(x => x.AchievementName == Enum.GetName(typeof(AchievementType), Type));
        }

        public IEnumerable<Notification> GetNotifications(int UserId)
        {
            var User = GetUser(UserId);

            return GetNotifications(User);
        }

        public IEnumerable<Notification> GetNotifications(User User)
        {
            if (User != null)
            {
                if (User.Notifications.Count >= 1) return User.Notifications.AsEnumerable();
                else return null;
            }
            else return null;
        }

        public void ReadNotification(int UserId, int NotificationId)
        {
            var Notification = NotificationRepository.Get(NotificationId);

            if (Notification != null && Notification.UserId == UserId)
            {
                NotificationRepository.Delete(NotificationId);
            }
        }

        public void AddNotification(int UserId, string Message)
        {
            NotificationRepository.Add(new Notification
            {
                UserId = UserId,
                Message = Message,
                DateReceived = DateTime.Now
            });
        }
    }
}